﻿
using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Collections.Generic;

using System.Linq;
using System.Threading.Tasks;
using System.Web;

namespace Utilities.Redis
{
    public class RedisBaseHelp
    {
     

        public int DbNum { get; set; }

        private readonly ConnectionMultiplexer conn;

        public string CustomKeyPrex { get; set; }

        public static TimeSpan expiry = TimeSpan.FromMinutes(2);

        public ITransaction CreateTransaction()
        {
            return GetDatabase().CreateTransaction();
        }

        public IDatabase GetDatabase()
        {
            return conn.GetDatabase(DbNum);
        }

        public IServer GetServer(string serverPort)
        {
            return conn.GetServer(serverPort);
        }
     
        public RedisBaseHelp(){
            conn = RedisConnectionHelp.Instance;
        }
        private string AddKeyPrex(string tbkey)
        {
            var keyPrex = CustomKeyPrex ?? RedisConnectionHelp.SysCustomKeyPrex;
            return keyPrex +"_"+ tbkey;
        }

        public T Action<T>(Func<IDatabase,T> func) {
            var database = conn.GetDatabase(DbNum);
            return func(database);

        }

        private string ConvertJson<T>(T value)
        {
            string result = value is string ? value.ToString() : JsonConvert.SerializeObject(value);
            return result;
        }

        public T ConvertObj<T>(RedisValue value)
        {
            if (!value.HasValue)
            {
                return default(T);
            }
            return JsonConvert.DeserializeObject<T>(value);
        }

        private IList<T> ConvetList<T>(RedisValue[] values)
        {
            IList<T> result = new List<T>();
            foreach (var item in values)
            {
                var model = ConvertObj<T>(item);
                result.Add(model);
            }
            return result;
        }

        private RedisKey[] ConvertRedisKeys(List<string> redisKeys)
        {
            return redisKeys.Select(redisKey => (RedisKey)redisKey).ToArray();
        }

        /// <summary>
        /// 保存单个key值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="expiry">过期时间</param>
        /// <returns></returns>
        public bool StringSet(string key, string value, TimeSpan? expiry = default(TimeSpan?))
        {
            key = AddKeyPrex(key);
            return Action(db => db.StringSet(key, value, expiry));
        }
        /// <summary>
        /// 获取单个key的值
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public string StringGet(string key)
        {
            key = AddKeyPrex(key);
            return Action(db => db.StringGet(key));
        }

        /// <summary>
        /// 保存一个对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="obj"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public bool StringSet<T>(string key, T obj, TimeSpan? expiry = default(TimeSpan?))
        {
            key = AddKeyPrex(key);
           string json = ConvertJson<T>(obj);
            return Action(db => db.StringSet(key, json, expiry));
        }


        /// <summary>
        /// 获取一个key的对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public T StringGet<T>(string key)
        {
            key = AddKeyPrex(key);
            return Action(db => ConvertObj<T>(db.StringGet(key)));
        }

        #region List
        /// <summary>
        /// 移除指定ListId的内部List的值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void ListRemove<T>(string key, T value)
        {
            key = AddKeyPrex(key);
            Action(db => db.ListRemove(key, ConvertJson(value)));
        }


        /// <summary>
        /// 获取指定key的List
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public IList<T> listRange<T>(string key)
        {
            key = AddKeyPrex(key);
            return Action(redis =>
            {
                var values = redis.ListRange(key);
                return ConvetList<T>(values);
            });
        }

        /// <summary>
        /// 入队
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void ListRightPush<T>(string key, T value)
        {
            key = AddKeyPrex(key);
            Action(db => db.ListRightPush(key, ConvertJson<T>(value)));
        }


        /// <summary>
        /// 出队
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public T ListRightPop<T>(string key) where T : class
        {
            key = AddKeyPrex(key);
            return Action(db =>
            {
                var value = db.ListRightPop(key);
                return ConvertObj<T>(value);
            });
        }


        /// <summary>
        /// 入栈
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void ListLeftPush<T>(string key, T value)
        {
            key = AddKeyPrex(key);
            Action(db => db.ListLeftPush(key, ConvertJson(value)));
        }

        /// <summary>
        /// 出栈
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public T ListLeftPop<T>(string key) where T : class
        {
            key = AddKeyPrex(key);
            return Action(db =>
            {
                var value = db.ListLeftPop(key);
                return ConvertObj<T>(value);
            });
        }

        /// <summary>
        /// 获取集合中的数量
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public long ListLength(string key)
        {
            key = AddKeyPrex(key);
            return Action(redis => redis.ListLength(key));
        }


        #endregion

        #region HASH

        /// <summary>
        /// 判断某个数据是否已经被缓存
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <returns></returns>
        public bool HashExists(string key, string dataKey)
        {
            key = AddKeyPrex(key);
            return Action(db => db.HashExists(key, dataKey));
        }

        /// <summary>
        /// 存储数据到hash表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <param name="t"></param>
        /// <returns></returns>
        public bool HashSet<T>(string key, string dataKey, T t)
        {
            key = AddKeyPrex(key);
            return Action(db =>
            {
                string json = ConvertJson(t);
                return db.HashSet(key, dataKey, json);
            });
        }

        /// <summary>
        /// 移除hash中的某值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <returns></returns>
        public bool HashDelete(string key, string dataKey)
        {
            key = AddKeyPrex(key);
            return Action(db => db.HashDelete(key, dataKey));
        }

        /// <summary>
        /// 移除hash中的多个值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataKeys"></param>
        /// <returns></returns>
        public long HashDelete(string key, List<RedisValue> dataKeys)
        {
            key = AddKeyPrex(key);
            //List<RedisValue> dataKeys1 = new List<RedisValue>() {"1","2"};
            return Action(db => db.HashDelete(key, dataKeys.ToArray()));
        }

        /// <summary>
        /// 从hash表获取数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <returns></returns>
        public T HashGet<T>(string key, string dataKey)
        {
            key = AddKeyPrex(key);
            return Action(db =>
            {
                string value = db.HashGet(key, dataKey);
                return ConvertObj<T>(value);
            });
        }

        /// <summary>
        /// 为数字增长val
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <param name="val">可以为负</param>
        /// <returns>增长后的值</returns>
        public double HashIncrement(string key, string dataKey, double val = 1)
        {
            key = AddKeyPrex(key);
            return Action(db => db.HashIncrement(key, dataKey, val));
        }

        /// <summary>
        /// 为数字减少val
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <param name="val">可以为负</param>
        /// <returns>减少后的值</returns>
        public double HashDecrement(string key, string dataKey, double val = 1)
        {
            key = AddKeyPrex(key);
            return Action(db => db.HashDecrement(key, dataKey, val));
        }

        /// <summary>
        /// 获取hashkey所有Redis key
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public IList<T> HashKeys<T>(string key)
        {
            key = AddKeyPrex(key);
            return Action(db =>
            {
                RedisValue[] values = db.HashKeys(key);
                return ConvetList<T>(values);
            });
        }
        #endregion


        #region 有序set 
        /// <summary>
        /// 添加
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="score"></param>
        public bool SortedSetAdd<T>(string key, T value, double score)
        {
            key = AddKeyPrex(key);
            return Action(redis => redis.SortedSetAdd(key, ConvertJson<T>(value), score));
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public bool SortedSetRemove<T>(string key, T value)
        {
            key = AddKeyPrex(key);
            return Action(redis => redis.SortedSetRemove(key, ConvertJson(value)));
        }

        /// <summary>
        /// 获取全部
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public IList<T> SortedSetRangeByRank<T>(string key)
        {
            key = AddKeyPrex(key);
            return Action(redis =>
            {
                var values = redis.SortedSetRangeByRank(key);
                return ConvetList<T>(values);
            });
        }

        /// <summary>
        /// 获取集合中的数量
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public long SortedSetLength(string key)
        {
            key = AddKeyPrex(key);
            return Action(redis => redis.SortedSetLength(key));
        }

        #endregion


        #region key管理

        /// <summary>
        /// 删除单个key
        /// </summary>
        /// <param name="key">redis key</param>
        /// <returns>是否删除成功</returns>
        public bool KeyDelete(string key)
        {
            key = AddKeyPrex(key);
            return Action(db => db.KeyDelete(key));
        }

        /// <summary>
        /// 删除多个key
        /// </summary>
        /// <param name="keys">rediskey</param>
        /// <returns>成功删除的个数</returns>
        public long KeyDelete(List<string> keys)
        {
            List<string> newKeys = keys.Select(AddKeyPrex).ToList();
            return Action(db => db.KeyDelete(ConvertRedisKeys(newKeys)));
        }

        /// <summary>
        /// 判断key是否存储
        /// </summary>
        /// <param name="key">redis key</param>
        /// <returns></returns>
        public bool KeyExists(string key)
        {
            key = AddKeyPrex(key);
            return Action(db => db.KeyExists(key));
        }

        /// <summary>
        /// 重新命名key
        /// </summary>
        /// <param name="key">就的redis key</param>
        /// <param name="newKey">新的redis key</param>
        /// <returns></returns>
        public bool KeyRename(string key, string newKey)
        {
            key = AddKeyPrex(key);
            return Action(db => db.KeyRename(key, newKey));
        }

        /// <summary>
        /// 设置Key的时间
        /// </summary>
        /// <param name="key">redis key</param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public bool KeyExpire(string key, TimeSpan? expiry = default(TimeSpan?))
        {
            key = AddKeyPrex(key);
            return Action(db => db.KeyExpire(key, expiry));
        }

        #endregion

        #region Batch批量处理
        public IBatch CreateBatch()
        {
            return GetDatabase().CreateBatch();
        }


        public Task<bool> BatchHashSetAsync<T>(IBatch batch, string key, string dataKey, T t)
        {
            key = AddKeyPrex(key);
            var json = ConvertJson(t);
            return batch.HashSetAsync(key, dataKey, json);
        }


        public Task<bool> BatchSortedSetAdd<T>(IBatch batch, string key, T value, double score)
        {
            key = AddKeyPrex(key);
            var json = ConvertJson(value);
            return batch.SortedSetAddAsync(key, json, score);
        }


        public void BatchExecute(IBatch batch)
        {
            batch.Execute();
        }

        public Task<RedisValue> BatchHashGetAsync(IBatch batch, string key, string dataKey)
        {
            key = AddKeyPrex(key);
            return batch.HashGetAsync(key, dataKey);
        }


        public Task<RedisValue[]> BatchSortedSetRangeByRankAsync(IBatch batch, string key)
        {
            key = AddKeyPrex(key);
            return batch.SortedSetRangeByRankAsync(key);
        }


        #endregion

    

    }
}